﻿using GylCSharpUtils.Common.Base;
using GylCSharpUtils.Common.DataStruct;
using GylCSharpUtils.GSerializer.Abstract;
using GylCSharpUtils.GSerializer.Template;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;

namespace GylCSharpUtils.GSerializer.Utils {
    public class GSerialUtils : AGSerializer {

        public static BindingFlags SerialBindFlag { get {
                return AGSerializer.GSFieldBindFlag;
            } }
        
        public override object Deserialize(byte[] data) {
            throw new NotImplementedException();
        }

        // 序列化对象
        public override byte[] Serialize(object obj) {
            // 深度优先，递归获取所有的引用对象
            Dictionary<int, object> objPtrDict = new Dictionary<int, object>();
            List<GCHandle> handles = new List<GCHandle>();
            //GetAllObject(obj, objPtrDict, handles);


            return null;
        }
        /// <summary>
        /// 获取序列化基础信息
        /// </summary>
        /// <param name="rootId">根ID</param>
        /// <param name="rootObj">根对象</param>
        /// <param name="infoDict">封装信息字典，id -- 信息</param>
        /// <param name="infoDrawer">分类封装 type -- [信息1， 信息2]</param>
        /// <param name="refInfos">所有的引用信息</param>
        /// <param name="circularRefTables">循环引用对象表</param>
        public static void GetSerialBaseInfo(SerialObjID rootId, object rootObj, 
            out Dictionary<SerialObjID, ObjInfo> infoDict, 
            out Drawer<Type, ObjInfo> infoDrawer,
            out List<RefInfo> refInfos,
            out CirularRefTable circularRefTables) {
            infoDict = new Dictionary<SerialObjID, ObjInfo>();
            infoDrawer = new Drawer<Type, ObjInfo>();
            circularRefTables = new CirularRefTable();
            refInfos = new List<RefInfo>();
            List<object> pedigree = new List<object>();
            CheckAllChildObj(rootId, rootObj, infoDict, infoDrawer, refInfos, pedigree, 0, circularRefTables);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="id">对象标识，由父级提供</param>
        /// <param name="obj">对象</param>
        /// <param name="infoDict">标识字典，对应对象的封装信息</param>
        /// <param name="infoDrawer">分类字典，将封装信息按照对象的类型分类存放</param>
        /// <param name="refInfos">所有的引用信息</param>
        /// <param name="pedigree">前代列表</param>
        /// <param name="depth">调用深度</param>
        /// <param name="circularRefTables">循环引用表，所有循环引用的对象都在这里</param>
        public static SerialObjID CheckAllChildObj(SerialObjID id, object obj, 
            Dictionary<SerialObjID, ObjInfo> infoDict, 
            Drawer<Type, ObjInfo> infoDrawer,
            List<RefInfo> refInfos,
            List<object> pedigree, 
            int depth, 
            CirularRefTable circularRefTables) {
            var cid = HandleCircularRefs(id, obj, pedigree, circularRefTables);
            if(cid != null) return cid;
            // 没有循环引用，将自身加入列表中
            if(pedigree.Count <= depth) {
                pedigree.Add(obj);
            } else {
                pedigree[depth] = obj;
            }
            // 封装自己
            ObjInfo objInfo = new ObjInfo(id, obj);
            Type type = obj.GetType();
            // 将自身的封装信息放入存储
            infoDict.Add(id, objInfo);
            infoDrawer.Add(type, objInfo);
            // 判断是不是有专有转换器的对象,若是则直接返回自身id
            if(GSTManager.GetConverter(type)!=null) return id;
            ProcessFieldRefTarget(objInfo, infoDict, infoDrawer, refInfos, pedigree, depth, circularRefTables);
            
            // 返回自身ID
            return id;
        }

        /// <summary>
        /// 处理字段或属性的引用目标信息
        /// </summary>
        /// <param name="objInfo">自身的信息</param>
        /// <param name="infoDict">标识字典，对应对象的封装信息</param>
        /// <param name="infoDrawer">分类字典，将封装信息按照对象的类型分类存放</param>
        /// <param name="refInfos">所有的引用信息</param>
        /// <param name="pedigree">前代列表</param>
        /// <param name="depth">调用深度</param>
        /// <param name="circularRefTables">循环引用表，所有循环引用的对象都在这里</param>
        public static void ProcessFieldRefTarget(ObjInfo objInfo, Dictionary<SerialObjID, ObjInfo> infoDict,
            Drawer<Type, ObjInfo> infoDrawer,
            List<RefInfo> refInfos,
            List<object> pedigree,
            int depth,
            CirularRefTable circularRefTables) {
            var obj = objInfo.Obj;
            var id = objInfo.sID;
            var type = obj.GetType();
            // 反射获取所有的引用属性
            GSTManager.GetCacheInfo(type);
            var template = GSTManager.GetGSTemplate(type);
            // 获取所有引用的子对象的id并封装引用
            var fis = template.FieldInfoList;
            for(int i=0; i< fis.Length; i++) {
                var fi = fis[i];
                if(fi.IsValueType) continue;
                object childObj = fi.Getter(obj);
                if(childObj == null) continue;
                // 获取子对象第一次出现的序号
                SerialObjID childSid = CheckAllChildObj(id.Child(i), childObj, infoDict, infoDrawer, refInfos, pedigree, depth + 1, circularRefTables);
                // 在封装对象中添加对引用的封装
                RefInfo refInfo = objInfo.AddReferInfo(fi.Name, id, childSid);
                // 将引用信息装入列表
                refInfos.Add(refInfo);
            }
            //template.GetFieldInfo(out string[] nameArr, out byte[] flagArr, out _);
            //for(int i = 0; i < template.FieldNum; i++) {
            //    byte flag = flagArr[i];
            //    if(flag % 2 == 0) continue; // 最低为为0表示是值类型
            //    object childObj = GetValueFromFieldOrProp(obj, (flag >> 1) == 1, nameArr[i]);
            //    if(childObj == null) continue;
            //    // 获取子对象第一次出现的序号
            //    SerialObjID childSId = CheckAllChildObj(id.Child(i), childObj, infoDict, infoDrawer, refInfos, pedigree, depth + 1, circularRefTables); ;
            //    RefInfo refInfo = objInfo.AddReferInfo(nameArr[i], id, childSId);
            //    // 在封装对象中添加引用的封装
            //    refInfos.Add(refInfo);
            //}
        }


        /// <summary>
        /// 处理循环引用，若检测到该对象中存在循环引用，则返回第一次出现的ID，若没有循环引用，则返回null
        /// </summary>
        /// <param name="id">现在的ID</param>
        /// <param name="obj">要检测的对象</param>
        /// <param name="pedigree">上级列表</param>
        /// <param name="circularRefTables">循环引用对象表</param>
        /// <returns></returns>
        public static SerialObjID HandleCircularRefs(SerialObjID id, object obj, List<object> pedigree, CirularRefTable circularRefTables) {
            // 首先判断本对象是否在循环引用表中,若在则直接返回第一次出现的ID
            var cid = circularRefTables.CheckObj(obj);
            if(cid != null) return cid;
            // 判断与父级之间是否有循环引用
            int index = pedigree.Count - 1;
            while(index >= 0) {
                // 如果有相等，则表示有循环引用
                if(obj == pedigree[index]) break;
                index--;
            }
            if(index != -1) {   // 表示有循环引用,将之间所有的对象全部放入循环引用表
                SerialObjID parent = id;
                for(int i = pedigree.Count - 1; i >= index; i--) {
                    parent = parent.Parent();
                    circularRefTables.AddCirularRefObj(parent, pedigree[i]);
                }
                // 最后得到的ID就是自身第一次出现时的ID
                return parent;
            }
            return null;
        }

        /// <summary>
        /// 从对象的某个字段或属性中获取信息
        /// </summary>
        /// <param name="obj">对象</param>
        /// <param name="isField">是否为字段</param>
        /// <param name="pofName">字段或属性名</param>
        /// <returns></returns>
        public static object GetValueFromFieldOrProp(object obj, bool isField, string pofName) {
            if(obj == null) return null;
            if(pofName == null) return null;
            Type type = obj.GetType();
            if(isField) {
                var field = type.GetField(pofName, GSFieldBindFlag);
                if(field == null) return null;
                return field.GetValue(obj);
            } else {
                var prop = type.GetProperty(pofName, GSFieldBindFlag);
                if(prop == null) return null;
                return prop.GetValue(obj);
            }
        } 

    }
}
